import {ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Inject, Injectable, Injector, Type} from '@angular/core';
import {DOCUMENT} from '@angular/common';
import {config} from 'rxjs';

export interface DialogPos {
  top: string;
  left: string;
  width: string;
  height: string;
}

export interface ChildConfig {
  inputs: object;
  outputs: object;
  position?: DialogPos;
}

@Injectable({
  providedIn: 'root'
})
export class DomService {
  private childComponentRef: ComponentRef<any>;

  /*
  先得到组件工厂，
  用组件工厂创建组件
  将组件插入到应用的组件树
   */
  constructor(
    private resolver: ComponentFactoryResolver,   // 得到Angular中所有组件创建的工厂
    private appRef: ApplicationRef,  // 得到Angular应用本身的引用
    private injector: Injector,
    @Inject(DOCUMENT) private document: Document  // JavaScript中的DOM
  ) {
  }

  /**
   * 增加组件
   */
  public appendComponentTo(
    parentId: string,
    child: Type<any>,
    childConfig: ChildConfig
  ): void {
    // 获取child类型的组件工厂，创建组件，传入依赖注入器
    const childComponentRef = this.resolver.resolveComponentFactory(child).create(this.injector);
    this.attachConfig(childConfig, childComponentRef);
    this.childComponentRef = childComponentRef;
    // 将组件放入应用，但未把组件视图插入视图
    this.appRef.attachView(childComponentRef.hostView);
    // 得到组件的HTML渲染模板的表达
    const childDOMElement = (childComponentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
    // 插入到父视图
    this.document.getElementById(parentId).appendChild(childDOMElement);
  }

  public attachConfig(childConfig: ChildConfig, componentRef: ComponentRef<any>): void {
    const inputs = childConfig.inputs;
    const outputs = childConfig.outputs;
    for (const key in inputs) {
      if (inputs.hasOwnProperty(key)) {
        const element = inputs[key];
        // 将组件中的key属性替换成外部config中的key对应的属性
        componentRef.instance[key] = element;
      }
    }

    for (const key in outputs) {
      if (outputs.hasOwnProperty(key)) {
        const element = outputs[key];
        componentRef.instance[key] = element;
      }
    }
  }

  /**
   * 移除组件
   */
  public removeComponent(): void {
    this.appRef.detachView(this.childComponentRef.hostView);
  }
}
